/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.reserve;

import java.util.Calendar;
import java.util.Collection;

import com.db4o.ObjectContainer;

public final class ReserveOperation {

	private final ReserveOperationType opType;
	private ReserveInfo targetRsv;
	private Collection<ReserveInfo> reallocated;
	private final Calendar opStartTime;
	private final boolean abortable;
	private static final transient ReserveOperation idleOpe = new ReserveOperation(ReserveOperationType.idle, null);

	/*
	 * For db4o callback method
	 */
	public void objectOnDelete(ObjectContainer container) {
		/*
		 * NOTE: ReserveInfo instances must be remain.
		 * DO NOT delete targetRsv and entries in reallocated.
		 * delete(reallocated) deletes a Collection object,
		 * not entries.
		 */
		container.delete(opType);
		container.delete(reallocated);
		container.delete(opStartTime);
	}

	private ReserveOperation(ReserveOperationType opType, ReserveInfo tgt, boolean abortable) {

		this.opType = opType;
		setTarget(tgt);
		this.reallocated = null;
		this.opStartTime = Calendar.getInstance();
		this.abortable = abortable;

	}

	private ReserveOperation(ReserveOperationType opType, ReserveInfo tgt) {

		this(opType, tgt, true);

	}

	private void setTarget(ReserveInfo tgt) {

		this.targetRsv = tgt;
		if (this.targetRsv != null) {
			this.targetRsv.setWaitCommit();
		}

	}

	static ReserveOperation getIdleInstance() {

		return idleOpe;

	}

	static ReserveOperation getReserveInstance() {

		return new ReserveOperation(ReserveOperationType.reserve, null);

	}

	void setTargetReserveInfo(ReserveInfo tgt) {

		assert(this.targetRsv == null);
		setTarget(tgt);

	}

	void setReallocatedReserveInfo(Collection<ReserveInfo> reallocated) {

		if (this.reallocated == null) {
			this.reallocated = reallocated;
		} else {
			// NOTE: modify may call reallocate twice
			assert(this.opType == ReserveOperationType.modify);
			this.reallocated.addAll(reallocated);
		}

	}

	static ReserveOperation getCancelInstance(ReserveInfo tgt, boolean abortable) {

		return new ReserveOperation(ReserveOperationType.cancel, tgt, abortable);

	}

	static ReserveOperation getModifyInstance(ReserveInfo tgt) {

		return new ReserveOperation(ReserveOperationType.modify, tgt);

	}

	static ReserveOperation getDestroyInstance(ReserveInfo tgt) {

		return new ReserveOperation(ReserveOperationType.destroy, tgt);

	}

	static ReserveOperation getRemoveInstance(ReserveInfo tgt) {

		return new ReserveOperation(ReserveOperationType.remove, tgt, false);

	}

	public ReserveOperationType getOperationType() {

		return opType;

	}

	ReserveId getReserveId() {

		return (targetRsv != null) ? targetRsv.getReserveId() : null;

	}

	public ReserveInfo getTargetReserveInfo() {

		return targetRsv;

	}

	Collection<ReserveInfo> getReallocatedReserveInfos() {

		return reallocated;

	}

	public Calendar getOperationStartTime() {

		return opStartTime;

	}

	public boolean isAbortable() {

		switch (getOperationType()) {
		case reserve:
		case cancel:
		case modify:
			return abortable;
		case destroy:
			Calendar now = Calendar.getInstance();
			Calendar rsvEnd = getTargetReserveInfo().getEndTime();
			return now.before(rsvEnd) ? abortable : false;
		case remove:
			return false;
		default:
			assert(false);
			return abortable;
		}

	}

	public String toString() {

		return "ReserveOperation type=" + opType;

	}

}
